// Copyright 2017 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT

#include "constants_priv.h"

#define IA32_GS_BASE              0xc0000101
#define INTERRUPT_ADDR(base, x)   (x - base + GUEST_ENTRY)
#define CODE_64_SELECTOR          0x8

.text

.macro init_interrupt_handling start
    lgdt INTERRUPT_ADDR(\start, gdtr_for_\start)
    lidt INTERRUPT_ADDR(\start, idtr_for_\start)
    mov $GUEST_ENTRY + 0x1000, %rsp
    jmp end_of_\start

int0_for_\start:
    movq $0, (EXIT_TEST_ADDR)

.align 8
gdt_for_\start:
    // Null entry.
    .8byte 0
    // CODE_64_SELECTOR
    .2byte 0xffff                                   // Limit 15:00
    .2byte 0x0000                                   // Base 15:00
    .byte  0x00                                     // Base 23:16
    .byte  0b10011010                               // P(1) DPL(00) S(1) 1 C(0) R(1) A(0)
    .byte  0b10101111                               // G(1) D(0) L(1) AVL(0) Limit 19:16
    .byte  0x0                                      // Base 31:24
gdtr_for_\start:
    .2byte gdtr_for_\start - gdt_for_\start - 1
    .8byte INTERRUPT_ADDR(\start, gdt_for_\start)

.align 8
idt_for_\start:
    .2byte INTERRUPT_ADDR(\start, int0_for_\start)  // Offset 15:00
    .2byte CODE_64_SELECTOR                         // Segment selector
    .byte  0                                        // IST(000)
    .byte  0b10001110                               // P(1) DPL(00) 0 Type(1110)
    .2byte 0                                        // Offset 31:16
    .4byte 0                                        // Offset 63:32
    .4byte 0                                        // Reserved
idtr_for_\start:
    .2byte idtr_for_\start - idt_for_\start - 1     // Limit
    .8byte INTERRUPT_ADDR(\start, idt_for_\start)   // Address
end_of_\start:
.endm

// Test vcpu_resume.
FUNCTION(vcpu_resume_start)
    // Test that we do not exit on load/store of CR3.
    mov %cr3, %rax
    mov %rax, %cr3

    // Test that we do not exit on store of GS_BASE.
    xor %eax, %eax
    xor %edx, %edx
    mov $IA32_GS_BASE, %ecx
    wrmsr

    // Test that we handle CPUID instruction correctly.
    xor %eax, %eax
    cpuid

    movq $0, (EXIT_TEST_ADDR)
FUNCTION(vcpu_resume_end)

// Test vcpu_interrupt.
FUNCTION(vcpu_interrupt_start)
    init_interrupt_handling vcpu_interrupt_start
    sti
    jmp .
FUNCTION(vcpu_interrupt_end)

// Test guest HLT instruction handling.
FUNCTION(vcpu_hlt_start)
    init_interrupt_handling vcpu_hlt_start
    sti
    hlt
FUNCTION(vcpu_hlt_end)

// Test vcpu_read_state and vcpu_write_state.
FUNCTION(vcpu_read_write_state_start)
    add $1, %rax
    add $2, %rcx
    add $3, %rdx
    add $4, %rbx
    add $5, %rsp
    add $6, %rbp
    add $7, %rsi
    add $8, %rdi
    add $9, %r8
    add $10, %r9
    add $11, %r10
    add $12, %r11
    add $13, %r12
    add $14, %r13
    add $15, %r14
    add $16, %r15

    stc  // Set carry flag (bit 0)
    stac // Set AC flag (bit 18)

    movq $0, (EXIT_TEST_ADDR)
FUNCTION(vcpu_read_write_state_end)

// Test guest_set_trap using a memory-based trap.
FUNCTION(guest_set_trap_start)
    movq $0, (TRAP_ADDR)
    movq $0, (EXIT_TEST_ADDR)
FUNCTION(guest_set_trap_end)

// Test guest_set_trap using an IO-based trap.
FUNCTION(guest_set_trap_with_io_start)
    out %al, $TRAP_PORT
    movq $0, (EXIT_TEST_ADDR)
FUNCTION(guest_set_trap_with_io_end)
